home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 10 - 1994 / 10.12 Dec 94 / ControlStrip / SimpleCSClock.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-09  |  11.8 KB  |  427 lines  |  [TEXT/MPS ]

  1. /****************************************************************************************/
  2. /*                                                                                        */
  3. /*    SimpleCSClock - A simple control strip clock module                                    */
  4. /*                                                                                        */
  5. /*    By Mike Blackwell, mkb@cs.cmu.edu                                                    */
  6. /*                                                                                        */
  7. /****************************************************************************************/
  8.  
  9. #include <GestaltEqu.h>
  10. #include <Fonts.h>
  11. #include <Memory.h>
  12. #include <Packages.h>
  13. #include <Resources.h>
  14. #include <Strings.h>
  15. #include <SysEqu.h>
  16. #include <ToolUtils.h>
  17. #include "ControlStrip.h"
  18. #include "SimpleCSClock.h"
  19.  
  20.  
  21. // How often to check if the display needs to be updated (in ticks)
  22. #define INTERVAL    (2 * 60)                        // 2 seconds
  23.  
  24. // Display font information
  25. #define DISPLAY_FONT            monaco
  26. #define DISPLAY_FONT_SIZE        9
  27. #define DISPLAY_FONT_FACE        0
  28. #define DISPLAY_MARGIN            3                    // Blank space to left and right of text
  29.  
  30. typedef struct Globals {
  31.     PicHandle        arrowPicture;                    // Picture to show we have a popup menu
  32.     short            arrowWidth, arrowHeight;        // Size of arrow
  33.     short            fontHeight;                        // Ascender height of display font
  34.     Handle            helpStrings;                    // Balloon help strings for each state
  35.     MenuHandle        configMenu;                        // Menu to select display options
  36.     int                width;                            // Width of display
  37.     Boolean            show12Hour;                        // True for 12 hour display
  38.     int                displayTime;                    // Time that is currently being displayed
  39.                                                     //  (in minutes since midnight)
  40.     unsigned long    nextTick;                        // When to next update the display
  41. } Globals, *GlobalPtr, **GlobalHandle;
  42.  
  43.  
  44. typedef struct SavedSettings {
  45.     OSType            signature;                        // Signature to verify that prefs are for
  46.                                                     //  this module
  47.     Boolean            show12Hour;                        // True for 12 hour display
  48. } SavedSettings;
  49.  
  50.  
  51. long        Initialize(void);
  52. void        CleanUp(GlobalHandle globHand);
  53. long        HandleMouseClick(GlobalPtr globPtr, Rect *statusRect);
  54. short        SavePreferences(GlobalPtr globPtr);
  55. Boolean        UpdateTime(GlobalPtr globPtr);
  56. int            DrawDisplay(GlobalPtr globPtr, Rect *statusRect, Boolean drawit);
  57. Boolean        CheckFeatures(void);
  58.  
  59.  
  60. pascal long main(long message, long params, Rect *statusRect, GrafPtr statusPort)
  61. {
  62. #pragma unused(statusPort)
  63.     char            savedState;
  64.     GlobalPtr        globPtr;
  65.     long            result;
  66.     int                current_width;
  67.     Str255            helpString;
  68.  
  69.     if (params > 0) {                                    // If we have globals allocated,
  70.         savedState = HGetState((Handle)params);            //  save the locked/unlocked state,
  71.         HLock((Handle)params);                            //  lock the handle to the globals,
  72.         globPtr = *(GlobalHandle)params;                    //  and point to globals directly
  73.     }
  74.  
  75.     result = 0;                                            // Return zero for unknown messages
  76.  
  77.     switch (message) {
  78.  
  79.         case sdevInitModule:                            // Initialize the module
  80.             result = Initialize();
  81.             break;
  82.  
  83.         case sdevCloseModule:                            // Clean up before being closed
  84.             CleanUp((GlobalHandle)params);
  85.             params = 0L;                                // Handle is gone now
  86.             break;
  87.  
  88.         case sdevFeatures:                                // Return feature bits
  89.             result = (1 << sdevWantMouseClicks)    | \
  90.                      (1 << sdevDontAutoTrack)    | \
  91.                      (1 << sdevHasCustomHelp);
  92.             break;
  93.  
  94.         case sdevGetDisplayWidth:                        // Return display width
  95.             result = globPtr->width;
  96.             break;
  97.  
  98.         case sdevPeriodicTickle:                        // Periodic tickle when nothing else is happening
  99.             if (TickCount() >= globPtr->nextTick) {        // Time to update display yet?
  100.                 if (UpdateTime(globPtr)) {                // Check if time has changed
  101.                     EraseRect(statusRect);                // Yep, erase the old
  102.                     current_width = DrawDisplay(globPtr, statusRect, true);    // And draw the new
  103.                     // If the display width changed, let the control strip know
  104.                     if (globPtr->width != current_width) {
  105.                         globPtr->width = current_width;
  106.                         result = (1 << sdevResizeDisplay);
  107.                     }
  108.                 }
  109.                 globPtr->nextTick = TickCount() + INTERVAL;
  110.             }
  111.             break;
  112.  
  113.         case sdevDrawStatus:                            // Update the display
  114.             (void)DrawDisplay(globPtr, statusRect, true);
  115.             break;
  116.  
  117.         case sdevMouseClick:                            // User clicked on the module's display area in the status bar
  118.             result = HandleMouseClick(globPtr, statusRect);
  119.             break;
  120.  
  121.         case sdevSaveSettings:                            // Save changed settings
  122.             result = SavePreferences(globPtr);
  123.             break;
  124.  
  125.         case sdevShowBalloonHelp:                        // Display custom balloon help
  126.             SBGetDetachedIndString(&helpString, globPtr->helpStrings,
  127.                 globPtr->show12Hour ? kHelp12StringStr : kHelp24StringStr);
  128.             SBShowHelpString(statusRect, &helpString);
  129.             break;
  130.     }
  131.  
  132.     if ((long)params > 0)                                // If we have globals allocated,
  133.         HSetState((Handle)params, savedState);            //  restore the locked/unlocked state
  134.  
  135.     return(result);
  136. }
  137.  
  138.  
  139. long Initialize(void)
  140. {
  141.     long            result;
  142.     GlobalPtr        globPtr;
  143.     GlobalHandle    globHand;
  144.     FontInfo        fontInfo;
  145.     Str255            prefsResourceName;
  146.     SavedSettings    **preferences;
  147.  
  148.     result = -1;                                        // Assume failure
  149.  
  150.     if (!CheckFeatures()) return(result);
  151.  
  152.     if (! (globHand = (GlobalHandle)NewHandleClear(sizeof(Globals))))
  153.         goto done;                                        // Allocate the globals
  154.  
  155.     HLock((Handle)globHand);                            // Lock the globals while using them
  156.     globPtr = *globHand;                                //  and get a pointer to them
  157.  
  158. //  Load and detach the ‘up arrow’ picture
  159.  
  160.     if (! (globPtr->arrowPicture = GetPicture(kArrowPictID))) goto done;
  161.     DetachResource((Handle)globPtr->arrowPicture);
  162.  
  163. //    Compute size of arrow picture
  164.  
  165.     globPtr->arrowHeight = (**globPtr->arrowPicture).picFrame.bottom -
  166.                            (**globPtr->arrowPicture).picFrame.top;
  167.     globPtr->arrowWidth = (**globPtr->arrowPicture).picFrame.right -
  168.                           (**globPtr->arrowPicture).picFrame.left;
  169.  
  170. //    Compute size of display font
  171.  
  172.     TextFont(DISPLAY_FONT);
  173.     TextSize(DISPLAY_FONT_SIZE);
  174.     TextFace(DISPLAY_FONT_FACE);
  175.     GetFontInfo(&fontInfo);
  176.     globPtr->fontHeight = fontInfo.ascent;
  177.  
  178. //  Load and detach the configuration menu
  179.  
  180.     if (! (globPtr->configMenu = GetMenu(kConfigMenuID))) goto done;
  181.     DetachResource((Handle)globPtr->configMenu);
  182.  
  183. //  Load and detach the help strings
  184.  
  185.     if (! (globPtr->helpStrings = Get1Resource('STR#', kHelpStringsID))) goto done;
  186.     DetachResource(globPtr->helpStrings);
  187.  
  188. //  Get the module's saved preferences, if any, and configure the module
  189.  
  190.     SBGetDetachedIndString(&prefsResourceName, globPtr->helpStrings, kPrefNameStr);
  191.     if (! SBLoadPreferences(&prefsResourceName, (Handle *)&preferences) &&
  192.         ((**preferences).signature == kSignature)) {
  193.         globPtr->show12Hour = (**preferences).show12Hour;
  194.     }
  195.  
  196.     globPtr->nextTick = 0;                                // Do first update right away
  197.     globPtr->width = 0;
  198.     globPtr->displayTime = 0;                            // Haven't displayed a time yet
  199.  
  200.     HUnlock((Handle)globHand);                            // Unlock the globals
  201.  
  202.     result = (long)globHand;                            // Return the handle to the globals as the result
  203.  
  204. done:
  205.     return(result);                                        // Return either a handle or an error code
  206. }
  207.  
  208.  
  209. void CleanUp(GlobalHandle globHand)
  210. {
  211.     GlobalPtr        globPtr;
  212.  
  213.     if ((long)globHand <= 0) return;
  214.  
  215.     HLock((Handle)globHand);
  216.     globPtr = *globHand;
  217.  
  218.     if (globPtr->arrowPicture) DisposeHandle((Handle)globPtr->arrowPicture);
  219.  
  220.     if (globPtr->configMenu) DisposeMenu(globPtr->configMenu);
  221.  
  222.     if (globPtr->helpStrings) DisposeHandle(globPtr->helpStrings);
  223.  
  224.     DisposeHandle((Handle)globHand);
  225. }
  226.  
  227.  
  228. long HandleMouseClick(GlobalPtr globPtr, Rect *statusRect)
  229. {
  230.     short            menuItem;
  231.     long            result;
  232.     int                new_width;
  233.     Boolean            mode_changed;
  234.  
  235.     // Check off the appropriate items in the popup menu
  236.  
  237.     if (globPtr->show12Hour) {
  238.         SetItemMark(globPtr->configMenu, k12HourCmd, sdevMenuItemMark);
  239.         SetItemMark(globPtr->configMenu, k24HourCmd, noMark);
  240.     } else {
  241.         SetItemMark(globPtr->configMenu, k24HourCmd, sdevMenuItemMark);
  242.         SetItemMark(globPtr->configMenu, k12HourCmd, noMark);
  243.     }
  244.  
  245.     result = 0;
  246.  
  247.     // Display the popup menu
  248.  
  249.     menuItem = SBTrackPopupMenu(statusRect, globPtr->configMenu);
  250.  
  251.     // Handle the menu selection
  252.  
  253.     mode_changed = false;
  254.  
  255.     switch (menuItem) {
  256.         case k12HourCmd:
  257.             if (!globPtr->show12Hour) {
  258.                 globPtr->show12Hour = true;
  259.                 mode_changed = true;
  260.             }
  261.             break;
  262.  
  263.         case k24HourCmd:
  264.             if (globPtr->show12Hour) {
  265.                 globPtr->show12Hour = false;
  266.                 mode_changed = true;
  267.             }
  268.             break;
  269.     }
  270.  
  271.     // If the mode changed, calculate new display width and let CS know what happened
  272.  
  273.     if (mode_changed) {
  274.         result = (1 << sdevNeedToSave) | (1 << sdevHelpStateChange);
  275.         new_width = DrawDisplay(globPtr, statusRect, false);
  276.         if (globPtr->width != new_width) {
  277.             globPtr->width = new_width;
  278.             result |= (1 << sdevResizeDisplay);
  279.         }
  280.     }
  281.  
  282.     return(result);
  283. }
  284.  
  285.  
  286. short SavePreferences(GlobalPtr globPtr)
  287. {
  288.     short            result;
  289.     SavedSettings    **preferences;
  290.     Str255            prefsResourceName;
  291.  
  292.     preferences = (SavedSettings**)NewHandle(sizeof(SavedSettings));
  293.  
  294.     if (! (result = MemError())) {                    // Allocate a block to hold the settings
  295.  
  296.         (**preferences).signature = kSignature;        // Include a signature to verify it's ours
  297.         (**preferences).show12Hour = globPtr->show12Hour;
  298.  
  299.         // Get the name of the preferences resource
  300.         SBGetDetachedIndString(prefsResourceName, globPtr->helpStrings, kPrefNameStr);
  301.  
  302.         // Save the settings in the Control Strip's preferences file
  303.         result = SBSavePreferences(prefsResourceName, (Handle)preferences);
  304.  
  305.         DisposeHandle((Handle)preferences);            // Get rid of the block
  306.     }
  307.  
  308.     return(result);
  309. }
  310.  
  311.  
  312. // Compute the current time in seconds since midnight. If it's changed since we
  313. // last displayed the time, return true.
  314.  
  315. Boolean UpdateTime(GlobalPtr globPtr)
  316. {
  317.     unsigned long    now;
  318.     int                dispTime;
  319.  
  320.     GetDateTime(&now);                            // Get seconds since epoch
  321.     dispTime = (now % (60 * 60 * 24)) / 60;        // Compute minutes since midnight
  322.     if (dispTime != globPtr->displayTime) {        // Has the time changed yet?
  323.         globPtr->displayTime = dispTime;        // Yes
  324.         return(true);                            // Need to update display
  325.     } else {
  326.         return(false);
  327.     }
  328. }
  329.  
  330.  
  331. // Stuff the ascii string representing the number in to the destination string.
  332. // Number range is 0 - 99. If pad is true, pad with a zero if necessary.
  333. // Return pointer to end of string.
  334.  
  335. char *NumStr(char *dest, int num, Boolean pad)
  336. {
  337.     if (num < 0) num = 0;
  338.     if (num > 99) num = 99;
  339.     if (num >= 10) {
  340.         *dest++ = (num / 10) + '0';
  341.         num %= 10;
  342.     } else if (pad) {
  343.         *dest++ = '0';
  344.     }
  345.     *dest++ = num + '0';
  346.     *dest = '\0';
  347.     return(dest);
  348. }
  349.  
  350.  
  351. // Draw the time specified in displayTime. If drawit is false, don't actually
  352. // do any drawing. Returns the width of the display
  353.  
  354. int DrawDisplay(GlobalPtr globPtr, Rect *statusRect, Boolean drawit)
  355. {
  356.     int                result;
  357.     char            buf[10], *bptr;
  358.     int                hours, minutes;
  359.     Boolean            afternoon;
  360.     int                width, offset;
  361.     Rect            arrowRect;
  362.  
  363.     result = 0;
  364.  
  365.     hours = globPtr->displayTime / 60;
  366.     if (globPtr->show12Hour) {
  367.         afternoon = (hours >= 12);
  368.         if (afternoon) hours -= 12;
  369.         if (hours == 0) hours = 12;
  370.     }
  371.     minutes = globPtr->displayTime % 60;
  372.  
  373.     bptr = buf;
  374.  
  375.     if (globPtr->show12Hour) {
  376.         bptr = NumStr(bptr, hours, false);
  377.         *bptr++ = ':';
  378.         bptr = NumStr(bptr, minutes, true);
  379.         *bptr++ = (afternoon) ? 'P' : 'A';
  380.         *bptr++ = 'M';
  381.         *bptr = '\0';
  382.     } else {
  383.         bptr = NumStr(bptr, hours, true);
  384.         *bptr++ = ':';
  385.         bptr = NumStr(bptr, minutes, true);
  386.         *bptr = '\0';
  387.     }
  388.     c2pstr(buf);
  389.  
  390.     // Draw the time string a little away from the right edge and centered vertically
  391.     // Compute top/bottom margin. -1 is tweak to make it look just right...
  392.     TextFont(DISPLAY_FONT);
  393.     TextSize(DISPLAY_FONT_SIZE);
  394.     TextFace(DISPLAY_FONT_FACE);
  395.     if (drawit) {
  396.         offset = (statusRect->bottom - statusRect->top - globPtr->fontHeight) / 2 - 1;
  397.         MoveTo(statusRect->left + DISPLAY_MARGIN, statusRect->top + offset + globPtr->fontHeight);
  398.         DrawString(buf);
  399.     }
  400.  
  401.     width = DISPLAY_MARGIN + StringWidth(buf) - 1;
  402.     
  403.     // Draw the right arrow to show that the module has a popup menu
  404.     if (drawit) {
  405.         arrowRect.left = statusRect->left + width;
  406.         arrowRect.right = arrowRect.left + globPtr->arrowWidth;
  407.         arrowRect.top = statusRect->top +
  408.                         (statusRect->bottom - statusRect->top - globPtr->arrowHeight) / 2;
  409.         arrowRect.bottom = arrowRect.top + globPtr->arrowHeight;
  410.         DrawPicture(globPtr->arrowPicture, &arrowRect);
  411.     }
  412.  
  413.     width += globPtr->arrowWidth;
  414.  
  415.     return(width);
  416. }
  417.  
  418.  
  419. // Check if this machine is capable of running this control strip (probably
  420. // by using Gestalt). This simple example will run on any Mac, so just return
  421. // true.
  422.  
  423. Boolean CheckFeatures(void)
  424. {
  425.     return(true);
  426. }
  427.